﻿// -----------------------------------------------------------------------
// <copyright file="VariableLengthIntX{T,TType}.cs" company="none">
// Copyright (c) 2012 M. Alberti, xanatos(at)live.it
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// </copyright>
// -----------------------------------------------------------------------

namespace FluentSerializer.CollectionsHelpers
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using FluentSerializer.Utilities;

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TType"></typeparam>
    public class VariableLengthIntX<T, TType> : ICollectionHelper
    {
        /// <summary>
        /// 
        /// </summary>
        protected static readonly MethodInfo Read = typeof(BinaryReaderStream).GetMethod("Read" + typeof(TType).Name, BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);

        /// <summary>
        /// 
        /// </summary>
        protected static readonly MethodInfo Write = typeof(BinaryWriterStream).GetMethod("Write", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(TType) }, null);

        /// <summary>
        /// 
        /// </summary>
        protected static readonly Expression SizeOf = Expression.Constant(typeof(TType) != typeof(char) ? Marshal.SizeOf(typeof(TType)) : sizeof(char));

        /// <summary>
        /// 
        /// </summary>
        protected static readonly bool IsSigned = Serializer.IsConvertible(Expression.Constant(-1), typeof(TType));

        /// <summary>
        /// 
        /// </summary>
        static VariableLengthIntX()
        {
            if (Read == null || Write == null)
            {
                throw new NotSupportedException(typeof(TType).FullName);
            }
        }

        #region IFluentSerializerCollectionHandler Members

        /// <summary>
        /// 
        /// </summary>
        public bool IsUniversal
        {
            get { return true; }
        }

        /// <summary>
        /// 
        /// </summary>
        public bool IsFixedLength
        {
            get { return false; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1118:ParameterMustNotSpanMultipleLines", Justification = "More readable this way")]
        public Expression DeserializeLength(Expression reader, Expression length)
        {
            ParameterExpression length2 = Expression.Variable(typeof(int));

            Expression expression = Expression.Call(reader, Read);

            if (typeof(TType) != typeof(int))
            {
                expression = Expression.ConvertChecked(expression, typeof(int));
            }

            if (IsSigned)
            {
                expression = Expression.Block(
                    new[] { length2 },
                    new Expression[] 
                {
                    Expression.Assign(length2, expression),
                    Expression.Condition(Expression.LessThan(length2, Serializer.Zero), Expression.Throw(Expression.New(typeof(FormatException)), typeof(int)), length2, typeof(int)),
                });
            }

            return expression;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public Expression SerializeLength(Expression writer, Expression length)
        {
            Expression expression = length;

            if (typeof(TType) != typeof(int))
            {
                expression = Expression.ConvertChecked(expression, typeof(TType));
            }

            return Expression.Call(writer, Write, expression);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public Expression SizeLength(Expression length)
        {
            return SizeOf;
        }

        #endregion
    }
}